#include <stdexcept>

namespace Pdf
{
    template <>
	inline Integer::Value   cast<Integer::Value>(Object::Ptr obj);

    template <>
	inline Real::Value      cast<Real::Value>(Object::Ptr obj);

    template <>
	inline String::Value    cast<String::Value>(Object::Ptr obj);

    template <>
    inline Reference::Value cast<Reference::Value>(Object::Ptr obj);

    // =====================================================================

    template <ObjectType Type>
    SimpleObjectT<Type>::SimpleObjectT()
    {    }

    template <ObjectType Type>
    SimpleObjectT<Type>::SimpleObjectT(const Value &src) : value(src)
    {    }

    template <ObjectType Type>
    typename SimpleObjectT<Type>::Value SimpleObjectT<Type>::get() const
    {    return value;    }

    template <ObjectType Type>
    void SimpleObjectT<Type>::set(const Value &src)
    {    value = src;    }

    // =====================================================================

    template <ObjectType Type>
    ContainerObjectT<Type>::ContainerObjectT()
    {    }

	template <ObjectType Type>
	ContainerObjectT<Type>::ContainerObjectT(const Value &src) : value(src)
	{    }

    template <ObjectType Type>
    ContainerObjectT<Type>::ContainerObjectT(const std::list<Object::Ptr> &src)
    {    init(this, src);    }

    template <ObjectType Type>
    Object::Ptr ContainerObjectT<Type>::get(const Index &idx)
    {    return value[idx];    }

    template <ObjectType Type>
    void ContainerObjectT<Type>::set(const Index &idx, Object::Ptr src)
    {    value[idx] = src;    }

    template <ObjectType Type>
    typename ContainerObjectT<Type>::SizeType ContainerObjectT<Type>::count() const
    {    return value.size();    }

    template <ObjectType Type>
    typename ContainerObjectT<Type>::Iterator ContainerObjectT<Type>::begin()
    {    return value.begin();    }

    template <ObjectType Type>
    typename ContainerObjectT<Type>::Iterator ContainerObjectT<Type>::end()
    {    return value.end();    }

    template <ObjectType Type>
    void ContainerObjectT<Type>::init(ContainerObjectT<Type> *obj, const std::list<Object::Ptr> &src)
    {
        obj->value.reserve( src.size() );
        std::copy( src.begin(), src.end(), std::back_inserter(obj->value) );
    }

    template <ObjectType Type>
    template <typename TargetType>
    typename TargetType::Ptr ContainerObjectT<Type>::get(const Index &idx)
    {
        Object::Ptr obj = get(idx);
        return TargetType::Ptr( obj ? cast<TargetType*>( obj ) : 0 );
    }


    inline void ContainerObjectT<objDictionary>::init(ContainerObjectT<objDictionary> *obj, const std::list<Object::Ptr> &src)
    {
        for (std::list<Object::Ptr>::const_iterator i = src.begin(), end = src.end(); i != end; ++i)
        {
            Object::Ptr key = *i;
            Object::Ptr val = (++i == end ? new Null() : *i);
            obj->value[ cast<std::string>( key.get() ) ] = val;
        }
    }

    // =====================================================================

    template <typename ObjectType>
    VisitableT<ObjectType>::VisitableT()
    {    }

    template <typename ObjectType>
    template <typename Arg1>
    VisitableT<ObjectType>::VisitableT(const Arg1 &arg1) : ObjectType(arg1)
    {    }

    template <typename ObjectType>
    template <typename Arg1, typename Arg2>
    VisitableT<ObjectType>::VisitableT(const Arg1 &arg1, const Arg2 &arg2) : ObjectType(arg1)
    {    }

    template <typename ObjectType>
    void VisitableT<ObjectType>::apply(Visitor *visitor)
    {    visitor->visited( this );    }

	template <typename ObjectType>
	VisitableT<ObjectType>* VisitableT<ObjectType>::clone()
	{    return new VisitableT( value );    }

    // =====================================================================

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Null *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Integer *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Real *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Keyword *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Comment *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Name *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::String *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Array *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Names *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Dictionary *obj)
    {    DefaultPolicy::visited(obj);    }

    template <typename DefaultPolicy>
    void VisitorT<DefaultPolicy>::visited(Pdf::Reference *obj)
    {    DefaultPolicy::visited(obj);    }

    // =====================================================================

    template <typename TargetType>
    static void __fastcall DefaultNone::visited(TargetType *) throw()
    {    }

    template <typename TargetType>
    static void __fastcall DefaultError::visited(TargetType *)
    {    throw std::runtime_error("unexpected object type");    }

    // =====================================================================

    template <typename TargetType>
    struct CasterT : public VisitorT<DefaultNone>
    {
        CasterT() throw() : target(0)                       {    }
        virtual void __fastcall visited(TargetType obj)     {    target = obj;    }
        TargetType              target;
    };

    struct IntegerCaster : public VisitorT<DefaultError>
    {
        IntegerCaster() throw() : target(0)                 {    }
        virtual void __fastcall visited(Integer *obj)       {    target = obj->get();    }
        virtual void __fastcall visited(Reference *obj)     {    target = obj->get().id;    }
		Integer::Value          target;
    };

    struct RealCaster : public VisitorT<DefaultError>
    {
        RealCaster() throw() : target(0)                    {    }
        virtual void __fastcall visited(Integer *obj)       {    target = static_cast<Real::Value>( obj->get() );    }
        virtual void __fastcall visited(Real *obj)          {    target = obj->get();    }
		Real::Value             target;
    };

    struct StringCaster : public VisitorT<DefaultError>
    {
        virtual void __fastcall visited(Keyword *obj)       {    target = obj->get();    }
        virtual void __fastcall visited(Name *obj)          {    target = obj->get();    }
        virtual void __fastcall visited(String *obj)        {    target = obj->get();    }
		String::Value           target;
    };

    struct RefCaster : public VisitorT<DefaultError>
    {
        virtual void __fastcall visited(Reference *obj)     {    target = obj->get();    }
        Reference::Value        target;
    };

    // =====================================================================

    template <typename TargetType>
    TargetType cast(Object::Ptr obj)
    {
        CasterT<TargetType> caster;
        obj->apply( &caster );
        return caster.target;
    }

    template <>
	inline Integer::Value cast<Integer::Value>(Object::Ptr obj)
    {
        IntegerCaster caster;
        obj->apply( &caster );
        return caster.target;
    }

    template <>
	inline Real::Value cast<Real::Value>(Object::Ptr obj)
    {
        RealCaster caster;
        obj->apply( &caster );
        return caster.target;
    }

    template <>
	inline String::Value cast<String::Value>(Object::Ptr obj)
    {
        StringCaster caster;
        obj->apply( &caster );
        return caster.target;
    }

    template <>
    inline Reference::Value cast<Reference::Value>(Object::Ptr obj)
    {
        RefCaster caster;
        obj->apply( &caster );
        return caster.target;
    }
}